#pragma once

#include <iostream>
#include <queue>
#include <pthread.h>

template<class T>
class BlockQueue
{
private:
    bool IsFull()
    {
        return _blockQueue.size() ==  _capacity;
    }
    bool IsEmpty()
    {
        return _blockQueue.empty();
    }
public:
    BlockQueue(int capacity)
        :_capacity(capacity), _productWaitNum(0), _consumWaitNum(0)
    {
        pthread_mutex_init(&_mutex, nullptr);
        pthread_cond_init(&_productCond, nullptr);
        pthread_cond_init(&_consumCond, nullptr);
    }
    void Enqueue(const T& in) //生产者接口
    {
        pthread_mutex_lock(&_mutex);
        // 这里必须使用while,防止线程伪唤醒， 伪唤醒：只有一个任务，缺叫醒了很多个消费者，如果不使用while,就会出错
        while(IsFull())
        {
            //等待  1.让线程阻塞挂起 2.释放当前持有锁 3.条件满足，返回之前，pthread_cond_wait要求线程争锁，争锁成功，方可返回，继续向下执行
            _productWaitNum++;
            pthread_cond_wait(&_productCond, &_mutex);
            _productWaitNum--;

        }
        _blockQueue.emplace(in);
        //通知消费者消费
        if(_consumWaitNum > 0)
            pthread_cond_signal(&_consumCond);
        pthread_mutex_unlock(&_mutex);
    }
    void Pop(T* out) //消费者接口
    {
        pthread_mutex_lock(&_mutex);
        while(IsEmpty())
        {
            _consumWaitNum++;
            pthread_cond_wait(&_consumCond, &_mutex);
            _consumWaitNum--;

        }
        *out = _blockQueue.front();
        _blockQueue.pop();
        //通知生产者生产
        if(_productWaitNum > 0)
            pthread_cond_signal(&_productCond);
        pthread_mutex_unlock(&_mutex);
    }
    ~BlockQueue()
    {
        pthread_mutex_destroy(&_mutex);
        pthread_cond_destroy(&_productCond);
        pthread_cond_destroy(&_consumCond);
    }
private:
    int _capacity;
    std::queue<T> _blockQueue;

    pthread_mutex_t _mutex;
    pthread_cond_t _productCond; //生产者条件变量
    pthread_cond_t _consumCond; //消费者条件变量

    int _productWaitNum; //消费者等待线程数量
    int _consumWaitNum; //生产者等待线程数量
};